import { LinkedList, LinkedListNode } from "../common/linked_list";
import { AddElementLog, ElementLog, RemoveElementLog } from "./element-log";
import { DslType, isNamedType } from "../type-server/type-definition";

// 基础元素定义
export interface SchemaElement {
    type: string;
    parentElement?: StructElement;
    data?: Record<string, any>;
}

export interface StructElement extends SchemaElement {
    // 设置日志收集器
    setLogs(logs: ElementLog[]): void;
    clearLogs(): void;

    // 获取子元素列表
    getChildren(): LinkedList<SchemaElement>;

    // 插入子元素
    insertChild(element: MainElement, prev?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement>;
    // 移除子元素
    removeChild(element: MainElement): void;

    // 插入nextLine元素
    insertNextLine(offset?: number, prev?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement>;
    // 查询分隔符元素
    insertSplitor(splitor: string, prev?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement>;
    // 直接移除子节点
    removeChildNode(node: LinkedListNode<SchemaElement>): void;
    // 直接插入子节点
    insertChildNode(node: LinkedListNode<SchemaElement>, prev?: LinkedListNode<SchemaElement>): void;

    /**
     * 更新元素的值
     */
    updateElementValue(node: LinkedListNode<SchemaElement>, newValue: string): void;
    
    /**
     * 完成当前节点
     * @param node 当前完成的节点
     */
    childComplete(node: LinkedListNode<SchemaElement>): void;
}

// MainElement定义
export interface MainElement extends SchemaElement {
    readonly?: boolean;
    index: number;
}
export const isMainElement = (element: SchemaElement): element is MainElement => {
    return element && element.hasOwnProperty('type') && element.hasOwnProperty('index');
}

export interface InputElement extends MainElement {
    inputValue: string;
}
export const isInputElement = (element: SchemaElement): element is InputElement => {
    return element && element.hasOwnProperty('inputValue');
}

export const TYPE_SYMBOL_SPLITOR = Symbol('splitor');
export interface SplitorElement extends SchemaElement {
    $type: Symbol;
}
export const isSplitElement = (element: SchemaElement): element is SplitorElement => {
    return element.hasOwnProperty('$type') && (element as any).$type === TYPE_SYMBOL_SPLITOR;
}

type Func<T> = () => T;
export const withLog = <T>(service: StructElement, logs: ElementLog[], func: Func<T>): T => {
    service.setLogs(logs);
    const result = func();
    service.clearLogs();
    return result;
}

// 实现公共的删除、跳转规则；
export interface RootElement extends StructElement {
    type: 'root';
}

export class DslRootElement implements RootElement {
    type: 'root' = 'root';
    rootType: DslType;
    typeElement: InputElement;
    valueElement?: MainElement;
    children: LinkedList<SchemaElement> = new LinkedList<SchemaElement>();
    logs?: ElementLog[];

    // 根据DslType初始化元素
    constructor(rootType: DslType) {
        this.rootType = rootType;

        const rootTypeName = isNamedType(rootType) ? rootType.typeName : '';
        const typeElement: InputElement = {
            type: 'type',
            inputValue: rootTypeName,
            readonly: (!!rootTypeName),
            parentElement: this,
            index: 1
        };
        
        this.typeElement = typeElement;
        this.children.setAfter(1, typeElement);
        if (isNamedType(rootType)) {
            const value = rootType.createInitElement(this);
            value.index = 2;
            value.parentElement = this;
            this.valueElement = value;
            this.children.setAfter(2, this.valueElement);
        }
    }
    childComplete(node: LinkedListNode<SchemaElement>): void {
        throw new Error("Method not implemented.");
    }
    
    insertSplitor(splitor: string, prev?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement> {
        throw new Error("Method not implemented.");
    }

    updateElementValue(node: LinkedListNode<SchemaElement>, newValue: string): void {
        // 1. 判断node是否是当前元素的子元素，并且是InputElement类型的
        // 2. 检查newValue是否合法，合法后更新newValue的值
        // 4. 触发其他的变更
        // 5. element内部记录一些值
        throw new Error("Method not implemented.");
    }

    getChildren(): LinkedList<SchemaElement> {
        return this.children;
    }

    setLogs(logs: ElementLog[]): void {
        this.logs = logs;
    }

    clearLogs(): void {
        this.logs = undefined;
    }

    insertChild(element: MainElement, prev?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement> {
        if (this.valueElement) {
            throw 'value element exists';
        }
        const elementType = element.type;
        if (elementType !== 'schemaObject' && elementType !== 'schemaArray' && elementType !== 'value') {
            throw new Error(`Invalid element type: ${elementType}`);
        }
        const valueElement = element;
        valueElement.parentElement = this;
        valueElement.index = 2;
        return this.children.setAfter(2, valueElement, prev);
    }

    // TODO 兼容log系统
    insertChildNode(node: LinkedListNode<SchemaElement>, prev?: LinkedListNode<SchemaElement>): void {
        throw new Error("Method not implemented.");
    }

    removeChild(element: MainElement): void {
        if (!this.valueElement || this.valueElement.index !== element.index) {
            throw new Error('Value element not found or does not match');
        }
        const node = this.children.getNode(element.index!);
        if (!node) {
            throw new Error('Node not found');
        }
        this.children.removeNode(node);
        this.valueElement = undefined;
        if (this.logs) {
            this.logs.push(new RemoveElementLog(node));
        }
    }
    
    insertNextLine(offset?: number, currentNode?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement> {
        const finalOffset = (offset === 0 || offset) ? offset : 1;
        const nextLineElement: SchemaNextLineElement = {
            type: 'nextLine',
            offset: finalOffset,
            parentElement: this
        };
        const node = this.children.insertBefore(nextLineElement, currentNode);
        if (this.logs) {
            this.logs.push(new AddElementLog(node.value, node));
        }
        return node;
    }

    removeChildNode(node: LinkedListNode<SchemaElement>): void {
        const element = node.value;
        if (this.valueElement && isMainElement(element) && element.index === this.valueElement.index) {
            this.valueElement = undefined;
        }
        this.children.removeNode(node);
        if (this.logs) {
            this.logs.push(new RemoveElementLog(node));
        }
    }
}

export class SimpleObjectElement implements StructElement {
    type: 'schemaObject' = 'schemaObject';
    parentElement?: StructElement;
    children: LinkedList<SchemaElement> = new LinkedList<SchemaElement>();
    index?: number;
    data?: Record<string, any>;
    readonly?: boolean;
    logs?: ElementLog[];

    constructor() {}
    
    insertSplitor(splitor: string, prev?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement> {
        throw new Error("Method not implemented.");
    }
    updateElementValue(node: LinkedListNode<InputElement>, newValue: string): void {
        throw new Error("Method not implemented.");
    }

    setLogs(logs: ElementLog[]): void {
        this.logs = logs;
    }

    clearLogs(): void { 
        this.logs = undefined;
    }

    insertNextLine(offset?: number, currentNode?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement> {
        const finalOffset = (offset === 0 || offset) ? offset : 1;
        const nextLineElement: SchemaNextLineElement = {
            type: 'nextLine',
            offset: finalOffset,
            parentElement: this
        };
        const node = this.children.insertBefore(nextLineElement, currentNode);
        if (this.logs) {
            this.logs.push(new AddElementLog(node.value, node));
        }
        return node;
    }
    
    removeChildNode(node: LinkedListNode<SchemaElement>): void {
        this.children.removeNode(node);
        if (this.logs) {
            this.logs.push(new RemoveElementLog(node));
        }
    }

    removeChild(element: MainElement): void {
        if (!isMainElement(element)) {
            return;
        }
        // 未加入到children中，直接返回
        if (!element.index) {
            return;
        }
        const node = this.children.getNode(element.index);
        if (!node) {
            return;
        }

        // 删除节点
        this.children.removeNode(node);
        if (this.logs) {
            this.logs.push(new RemoveElementLog(node));
        }
        if (element.type !== 'key') {
            return;
        }

        // 当前元素为key类型时，如果key元素前面存在分隔符，则删除分隔符
        let splitorNode: LinkedListNode<SchemaElement> | null = null;
        let prev = node.prev;
        while (prev) {
            if (prev.value.type === 'splitor') {
                splitorNode = prev;
            }
            if (prev.value.type === 'key') {
                break;
            }
            prev = prev.prev;
        }
        if (splitorNode) {
            this.children.removeNode(splitorNode);
            if (this.logs) {
                this.logs.push(new RemoveElementLog(splitorNode));
            }
        }
    }

    insertChild(element: SchemaElement, p?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement> {
        let node: LinkedListNode<SchemaElement>;
        if (isMainElement(element)) {
            const index = this.children.getNextId();
            node = this.children.setAfter(index, element, p);
            element.index = index;
            element.parentElement = this;
        } else {
            node = this.children.insertAfter(element, p);
        }
        if (this.logs) {
            this.logs.push(new AddElementLog(element, node));
        }

        if (element.type !== 'key') {
            return node;
        }

        let hasSplitor = false;
        let prev = node.prev;
        while (prev) {
            if (prev.value.type === 'splitor') {
                hasSplitor = true;
            }
            if (prev.value.type === 'key') {
                break;
            }
            prev = prev.prev;
        }
        if (hasSplitor) {
            return node;
        }
        
        prev = node.prev;
        while (prev) {
            if (isSplitElement(prev.value)) {
                prev = prev.prev;
            } else {
                this.insertChild({
                    type: 'splitor',
                    splitor: ',',
                } as SchemaSplitorElement, prev);
                break;
            }
        }
        return node;
    }

    insertChildNode(node: LinkedListNode<SchemaElement>, prev?: LinkedListNode<SchemaElement>): void {
        this.children.insertNodeAfter(node, prev);
        if (node.key) {
            this.children.set(node.key, node);
        }
    }
}

export class SimpleArrayElement implements StructElement {
    type: 'schemaArray' = 'schemaArray';
    children: LinkedList<SchemaElement> = new LinkedList<SchemaElement>();
    parentElement?: StructElement;

    index?: number | undefined;
    data?: Record<string, any> | undefined;
    readonly?: boolean;
    logs?: ElementLog[];

    constructor() {}
    insertSplitor(splitor: string, prev?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement> {
        throw new Error("Method not implemented.");
    }
    updateElementValue(node: LinkedListNode<InputElement>, newValue: string): void {
        throw new Error("Method not implemented.");
    }

    setLogs(logs: ElementLog[]): void {
        this.logs = logs;
    }

    clearLogs(): void {
        this.logs = undefined;
    }

    removeChild(element: MainElement): void {
        // 未加入到children中，直接返回
        if (!element.index) {
            return;
        }
        const node = this.children.getNode(element.index);
        if (!node) {
            return;
        }

        // 删除节点
        this.children.removeNode(node);
        if (this.logs) {
            this.logs.push(new RemoveElementLog(node));
        }
        if (element.type !== 'type') {
            return;
        }

        // 当前元素为type类型时，如果key元素前面存在分隔符，则删除分隔符
        let splitorNode: LinkedListNode<SchemaElement> | null = null;
        let prev = node.prev;
        while (prev) {
            if (prev.value.type === 'splitor') {
                splitorNode = prev;
            }
            if (prev.value.type === 'type') {
                break;
            }
            prev = prev.prev;
        }
        if (splitorNode) {
            this.children.removeNode(splitorNode);
            if (this.logs) {
                this.logs.push(new RemoveElementLog(splitorNode));
            }
        }
    }

    insertNextLine(offset?: number, currentNode?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement> {
        const finalOffset = (offset === 0 || offset) ? offset : 1;
        const nextLineElement: SchemaNextLineElement = {
            type: 'nextLine',
            offset: finalOffset,
            parentElement: this
        };
        const node = this.children.insertBefore(nextLineElement, currentNode);
        if (this.logs) {
            this.logs.push(new AddElementLog(node.value, node));
        }
        return node;
    }

    removeChildNode(node: LinkedListNode<SchemaElement>): void {
        this.children.removeNode(node);
        if (this.logs) {
            this.logs.push(new RemoveElementLog(node));
        }
    }
    
    insertChild(element: SchemaElement, p?: LinkedListNode<SchemaElement>): LinkedListNode<SchemaElement> {
        let node: LinkedListNode<SchemaElement>;
        if (isMainElement(element)) {
            const index = this.children.getNextId();
            node = this.children.setAfter(index, element, p);
            element.index = index;
            element.parentElement = this;
        } else {
            node = this.children.insertAfter(element, p);
        }
        if (this.logs) {
            this.logs.push(new AddElementLog(element, node));
        }

        if (element.type !== 'type') {
            return node;
        }

        let hasSplitor = false;
        let prev = node.prev;
        while (prev) {
            if (prev.value.type === 'splitor') {
                hasSplitor = true;
            }
            if (prev.value.type === 'type') {
                break;
            }
            prev = prev.prev;
        }
        if (hasSplitor) {
            return node;
        }
        
        prev = node.prev;
        while (prev) {
            if (isSplitElement(prev.value)) {
                prev = prev.prev;
            } else {
                this.insertChild({
                    type: 'splitor',
                    splitor: ',',
                } as SchemaSplitorElement, prev);
                break;
            }
        }
        return node;
    }

    insertChildNode(node: LinkedListNode<SchemaElement>, prev?: LinkedListNode<SchemaElement>): void {
        this.children.insertNodeAfter(node, prev);
        if (node.key) {
            this.children.set(node.key, node);
        }
    }
}

export interface SchemaNextLineElement extends SchemaElement {
    type: 'nextLine';
    // 相对父元素的缩进值
    offset: number;
}

// schema分隔符元素
export interface SchemaSplitorElement extends SchemaElement {
    type: 'splitor';
    splitor: string;
}
